package com.jfinal.plugin;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.setting.Setting;
import com.jfinal.kit.PathKit;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.OrderedFieldContainerFactory;
import com.jfinal.plugin.activerecord.dialect.Sqlite3Dialect;
import com.jfinal.plugin.druid.DruidPlugin;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

import java.net.URL;
import java.util.Set;
import java.util.regex.Pattern;

public class Sqlite3DataSourcePlugin implements IPlugin {

    protected Logger logger = LogManager.getLogger(getClass().getName());

    /**
     * 配置文件路径
     */
    private String configPath = "config/sqlite3.txt";

    private String stlRootPath = null;

    public Sqlite3DataSourcePlugin(){}

    public Sqlite3DataSourcePlugin(String configPath){
        this.configPath = configPath;
    }

    public Sqlite3DataSourcePlugin(String configPath, String stlRootPath){
        this.configPath = configPath;
        this.stlRootPath = stlRootPath;
    }

    public DruidPlugin createDruidPlugin(Setting config){
        String jdbcUrl = config.getStr("jdbcUrl").trim();
        jdbcUrl = jdbcUrl.replace("${webroot}",PathKit.getWebRootPath());
        jdbcUrl = jdbcUrl.replace("${classpath}",PathKit.getRootClassPath());
        logger.debug("[Sqlite3DataSourcePlugin] - createDruidPlugin > {}",jdbcUrl);
        DruidPlugin dp = new DruidPlugin(jdbcUrl, config.getStr("user"), config.getStr("password",""));
        return dp;
    }
    @SuppressWarnings("unchecked")
    public boolean start() {
        try {
            /* 读取配置文件 */
            Setting config = new Setting(configPath);
            /* 获得分组 */
            if(config.getGroups()!=null && config.getGroups().size() > 0){
                /* 循环分组 */
                boolean isFirst = true;//定义标记，主要用于下面的model绑定
                for(String group : config.getGroups()){
                    Setting setting = config.getSetting(group);
                    DruidPlugin dp = createDruidPlugin(setting);
                    dp.start();//直接启动插件
                    /* 定义插件 */
                    ActiveRecordPlugin arp = new ActiveRecordPlugin(group,dp);
                    /* 设置有序排序字段 */
                    arp.setContainerFactory(new OrderedFieldContainerFactory());
                    /* 设置sql模板基础路径 */
                    arp.setBaseSqlTemplatePath(PathKit.getRootClassPath());
                    /* 设置开发模式 */
                    if(setting.get("devMode")!=null && setting.getStr("devMode").trim().length() > 0){
                        arp.setDevMode(setting.getBool("devMode"));
                    }
                    if(setting.get("devMode")!=null && setting.getStr("devMode").trim().length() > 0){
                        arp.setShowSql(setting.getBool("devMode"));
                    }
                    arp.setDialect(new Sqlite3Dialect());
                    /* 设置 sql template 模板内置工具类 */
                    if(setting.get("sharedMethod")!=null && setting.getStr("sharedMethod").trim().length() > 0){
                        String[] classes = setting.getStrings("sharedMethod");
                        for(String cls : classes){
                            try {
                                Class _class = Class.forName(cls.trim());
                                arp.getEngine().addSharedMethod(_class.newInstance());
                            } catch (Exception e1) {
                                logger.error("[Sqlite3DataSourcePlugin] - 数据源 {} 加载 sharedMethod 方法失败，method : {} \r\n {}",group,cls, ExceptionUtil.getMessage(e1));
                            }
                        }
                    }
                    /**
                     * 扫描sql模板并加载
                     */
                    loadSqlTemplate(arp);
                    /**
                     * 扫描实体类并加载
                     */
                    Reflections _reflections = new Reflections(new ConfigurationBuilder().setUrls(ClasspathHelper.forPackage("")).filterInputsBy(new FilterBuilder().include(".+\\.model\\.[^\\.]+\\.class")));
                    Set<Class<?>> models = _reflections.getTypesAnnotatedWith(com.jfinal.annotation.Model.class);
                    if(models!=null){
                        for(Class _m : models){
                            logger.debug("[Sqlite3DataSourcePlugin] - 正在加载模型 {}",_m.getName());
                            com.jfinal.annotation.Model m = (com.jfinal.annotation.Model) _m.getAnnotation(com.jfinal.annotation.Model.class);
                            /* 如果模型数据源为空，就要判断当前数据源是否为第一个 */
                            if((group.equals(m.ds())||(StrUtil.isBlank(m.ds()) && isFirst))){
                                if(StrUtil.isBlank(m.key())){
                                    arp.addMapping(m.table(), _m);
                                }else{
                                    arp.addMapping(m.table(),m.key(),_m);
                                }
                            }
                        }
                    }
                    arp.start();
                    isFirst = false;
                }
            }
        }catch (Exception e) {
            e.printStackTrace();
            logger.error("[Sqlite3DataSourcePlugin] - 加载配置文件 {} 异常，初始化失败",configPath);
            return false;
        }
        logger.info("[Sqlite3DataSourcePlugin] - 插件初始化成功");
        return true;
    }

    public boolean stop() {
        return false;
    }

    /**
     * 加载SQL 模板文件
     * @param arp
     */
    public void loadSqlTemplate(ActiveRecordPlugin arp){
        Reflections reflections = null;
        if(stlRootPath == null){
            logger.debug("[Sqlite3DataSourcePlugin] - 采用默认方式获取stl查询地址 {}",ClasspathHelper.forPackage("/"));
            reflections = new Reflections(new ConfigurationBuilder().setUrls(ClasspathHelper.forPackage("/")).setScanners(new ResourcesScanner()));
        }else{
            try {
                reflections = new Reflections(new ConfigurationBuilder().setUrls(new URL("file:///"+stlRootPath)).setScanners(new ResourcesScanner()));
            } catch (Exception e) {
                logger.error("[Sqlite3DataSourcePlugin] - 初始化 sql template 加载根路径失败 : {}",stlRootPath);
            }
        }
        if(reflections!=null){
            Set<String> _sql = reflections.getResources(Pattern.compile(".*\\.stl"));
            if(_sql!=null){
                for(String sql : _sql){
                    arp.addSqlTemplate(sql);
                    logger.debug("[Sqlite3DataSourcePlugin] - 加载 sql template 模板 : {}",sql);
                }
            }
        }
    }
}
